import os
import time
from copy import deepcopy

import xlrd
from docx import Document
from docx.shared import Cm


class Const(object):
    """
    描述：全局配置
    """
    # 章节标题list
    functionPointLevel = ['一级功能', '二级功能', '三级功能', '四级功能', '五级功能', '六级功能', '七级功能', '八级功能']
    # 功能级别对应的标题级别
    titleLevel = [2, 3, 4, 5, 6, 7, 8, 9]
    # Excel数据源文件名称（即：待转成word的excel数据）
    excelFileName = '测试用例.xlsx'
    sheetName = '测试用例'
    # word表格模板文件名称（即：你要生成的表格模板）
    wordTableTemplateFileName = '用例表格模板.docx'
    # 输出模板（将excel数据转换成word后，拼接到该模板）
    exportTemplateFileName = '输出模板.docx'
    # 输出结果文件的前缀名称
    exportNamePrefix = '用例结果'
    # 图片文件夹
    imagesPath = 'Images'
    # 图片的宽度
    picWidth = 11.5


class Transfer(object):
    """
    描述：转换的具体操作类
    """
    @staticmethod
    def doTransfer(dataList, doc, table):
        # 添加功能用例
        for data in dataList:
            current = 0
            for point in Const.functionPointLevel:
                titleName = data.get(point)
                if titleName is not None and titleName != '' and titleName != '/':
                    Transfer.addTitle(doc, titleName, Const.titleLevel[current])  # 添加标题
                current += 1
            Transfer.addTable(doc, table)  # 添加用例表格并填充内容
            Transfer.replaceTableContent(doc, data)  # 对新添加的table进行内容匹配与替换

    @classmethod
    def addTitle(cls, doc, text, level):
        """
        描述：在document后追加标题
        :param doc: 文档对象
        :param text: 标题内容
        :param level: 标题级别
        """
        title = doc.add_heading(level=level)
        title.add_run(text)

    @classmethod
    def addTable(cls, doc, tableTemplate):
        """
        描述：添加一个表格模板，并拼接到输出模板的文档对象中
        :param doc: 输出模板文档对象
        :param tableTemplate: 表格模板
        """
        paragraphs = doc.paragraphs
        newTable = deepcopy(tableTemplate)
        paragraphs[len(paragraphs) - 1]._p.addnext(newTable._element)

    @classmethod
    def replaceTableContent(cls, doc, dictData):
        """
        描述：对最新的表格进行遍历及内容替换
        :param doc: 文档对象
        :param dictData: 当前行所对用的excel表格数据
        """
        newTable = doc.tables[len(doc.tables) - 1]
        rowCount = len(newTable.rows)
        colCount = len(newTable.columns)
        for r in range(rowCount):
            for c in range(colCount):
                cell = newTable.cell(r, c)
                if '$' not in cell.text:
                    continue

                for headName in Const.headerList:
                    varHead = '${' + headName + '}'
                    if varHead in cell.text:
                        # 获取原cell的原对其格式，再设置回去
                        alignment = cell.paragraphs[0].alignment
                        cell.text = cell.text.replace(varHead, dictData.get(headName))
                        # 包含图片时，处理
                        if '.jpg' in dictData.get(headName) or '.png' in dictData.get(headName) or '.JPG' in dictData.get(headName) or '.PNG' in dictData.get(headName):
                            Transfer.addPicture(cell, doc)
                        # 恢复单元格的对齐方式
                        for paragraph in cell.paragraphs:
                            paragraph.paragraph_format.alignment = alignment

    @staticmethod
    def addPicture(cell, doc):
        """
        描述：实际结果中会包含图片的情况，如果遇到图片则添加图片
        :param cell: 单元格
        :param cellText: cell文本内容
        :param doc: 文档对象
        """
        resultArr = cell.text.split('$')
        cell.text = ''  # 先把单元格原来的内容清空，再逐行加回去
        paragraph = cell.paragraphs[0]
        for data in resultArr:
            try:
                # 如果包含截图-，则处理截图
                if data.endswith('.jpg') or data.endswith('.png') or data.endswith('.PNG') or data.endswith('.JPG'):
                    run = paragraph.add_run()
                    run.add_picture(Const.imagesPath + os.sep + data)
                    current_index = len(doc.inline_shapes) - 1
                    # 设置图像的宽度
                    origin_w = doc.inline_shapes[current_index].width  # 获取原始宽度
                    doc.inline_shapes[current_index].width = Cm(Const.picWidth)  # 设置宽度
                    w = doc.inline_shapes[current_index].width  # 设置宽度后新的 宽度
                    h = doc.inline_shapes[current_index].height  # 获取原始 高度
                    doc.inline_shapes[current_index].height = int(h * w / origin_w)  # 设置新的高度
                else:
                    paragraph.add_run(data)
            except Exception as e:
                print(f"An error occurred: {e}")


class ExcelHelper(object):
    """
    描述：读取 Excel 表格数据的类
    """
    @staticmethod
    def read_by_list(excel_name, sheet_name):
        """
        作用：读取 Excel 表格中指定 sheet 的表格数据
        :param excel_name: Excel 文件名
        :param sheet_name: Sheet 名
        :return: 列表(sheet_data)
        """
        data_list = xlrd.open_workbook(excel_name)
        sheet = data_list.sheet_by_name(sheet_name)
        sheet_data = []
        for i in range(sheet.nrows):
            value = sheet.row_values(i)
            if type(value) == str:
                value = value.strip()
            sheet_data.append(value)
        return sheet_data

    @staticmethod
    def read_by_dict(excel_name, sheet_name):
        """
        作用：读取 Excel 表格中指定 sheet 的表格数据
        :param excel_name: Excel 文件名
        :param sheet_name: Sheet 名
        :return: 字典(dict_data)
        """
        st_data = ExcelHelper.read_by_list(excel_name, sheet_name)
        list_row1 = st_data[0]
        dict_data = []
        a = True
        for data in st_data:
            if a:
                a = False
                continue
            list_dict = dict(zip(list_row1, data))
            dict_data.append(list_dict)
        return dict_data


if __name__ == '__main__':
    # 程序开始：Start
    print('程序开始运行...')
    # 步骤一：读取Excel表格数据、Excel表格的表头
    excelData = ExcelHelper.read_by_dict(Const.excelFileName, Const.sheetName)
    Const.headerList = ExcelHelper.read_by_list(Const.excelFileName, Const.sheetName)[0]
    # 步骤二：读取待拼接的输出模板文档
    document = Document(Const.exportTemplateFileName)
    # 步骤三：读取表格模板
    tableDocument = Document(Const.wordTableTemplateFileName)
    # 步骤四：执行转换操作
    Transfer.doTransfer(excelData, document, tableDocument.tables[0])
    # 步骤五：将转换结果输出
    document.save(Const.exportNamePrefix + '-' + time.strftime('%Y%m%d%H%M', time.localtime(time.time())) + '.docx')
    # 程序结束：End
    print('程序运行结束...')
    print('请查看结果文件...')
